package com.rfid.reader;

import java.io.IOException;

import com.rfid.AppNotifyImpl.MRfidReaderNotifyImpl;

public class MRfidReader extends RfidReader {
	private byte[] readerId = null;

	public final static byte eTlvAttridle = (byte)0x00;
	public final static byte eTlvAttrEpc = (byte)0x01;
	public final static byte eTlvAttrUser = (byte)0x02;
	public final static byte eTlvAttrReserve = (byte)0x03;
	public final static byte eTlvAttrTid = (byte)0x04;
	public final static byte eTlvAttrRssi = (byte)0x05;
	public final static byte eTlvAttrTime = (byte)0x06;
	public final static byte eTlvAttrStatus = (byte)0x07;
	public final static byte eTlvAttrTagOperation = (byte)0x08;
	public final static byte eTlvAttrLocalAddr = (byte)0x09;
	public final static byte eTlvAttrAntNo = (byte)0x0A;
	public final static byte eTlvAttr6BData = (byte)0x10;
	public final static byte eTlvAttrSoftwareVersion = (byte)0x20;
	public final static byte eTlvAttrDevType = (byte)0x21;
	public final static byte eTlvAttrWorkParam = (byte)0x23;
	public final static byte eTlvAttrTransportParam = (byte)0x24;
	public final static byte eTlvAttrAdvanceParam = (byte)0x25;
	public final static byte eTlvAttrSingleParam = (byte)0x26;
	public final static byte eTlvAttrRelay = (byte)0x27;
	public final static byte eTlvAttrExtParam = (byte)0x29;
	public final static byte eTlvAttrSingleTagData = (byte)0x50;
	public final static byte eTlvAttrSingleRecord = (byte)0x51;
	public final static byte eTlvAttrDevNo = (byte)0x52;
	public final static byte eTlvAttrIccid = (byte)0x53;
	public final static byte eTlvAttrTempeture = (byte)0x70;


	private final static byte eCmdReset = (byte)0x10;
	private final static byte eCmdRestoreFactoryParam = (byte)0x12;
	private final static byte eCmdStartInventory = (byte)0x21;
	private final static byte eCmdInventoryOnce = (byte)0x22;
	private final static byte eCmdStopInventory = (byte)0x23;
	private final static byte eCmdGetBufferData = (byte)0x24;   //获取数据
	private final static byte eCmdWriteTag = (byte)0x30;
	private final static byte eCmdReadTagBlock = (byte)0x31;	//读取标签内的某个区域数据的值
	private final static byte eCmdWriteTagInWiegandMode = (byte)0x32;
	private final static byte eCmdLockTag = (byte)0x33;
	private final static byte eCmdDestoryTag = (byte)0x34;
	private final static byte eCmdWriteEpc = (byte)0x35; //将数据写入EPC区,并修改EPC长度
	private final static byte eCmdQueryExtParam = (byte)0x3E;
	private final static byte eCmdSetExtParam = (byte)0x3F;
	private final static byte eCmdQueryDeviceInfo = (byte)0x40;
	private final static byte eCmdSetWorkingParam = (byte)0x41;
	private final static byte eCmdQueryWorkingParam = (byte)0x42;
	private final static byte eCmdQueryTransportParam = (byte)0x43;
	private final static byte eCmdSetTransportParam = (byte)0x44;
	private final static byte eCmdQueryAdvanceParam = (byte)0x45;
	private final static byte eCmdSetAdvanceParam = (byte)0x46;
	private final static byte eCmdSetSingleParam = (byte)0x48;
	private final static byte eCmdQuerySingleParam = (byte)0x49;
	private final static byte eCmdQueryTime = (byte)0x4A;
	private final static byte eCmdSetTime = (byte)0x4B;
	private final static byte eCmdCtrlRelay = (byte)0x4C;
	private final static byte eCmdAudioPlay = (byte)0x4D;
	private final static byte eCmdVerifyAdd = (byte)0x4E;	//添加校验
	private final static byte eCmdSetUsbOutputDataParam = (byte)0x50;
	private final static byte eCmdQueryUsbOutputDataParam = (byte)0x51;
	private final static byte eCmdSetOutputDataFlag = (byte)0x52;
	private final static byte eCmdQueryOutputDataFlag = (byte)0x53;
	private final static byte eCmdSetModbusParam = (byte)0x54;
	private final static byte eCmdQueryModbusParam = (byte)0x55;
	private final static byte eCmdQueryRspTimes = (byte)0x71;
	private final static byte eCmdGetRecordData = (byte)0x72;
	private final static byte eCmdQueryFilterData = (byte)0x73;
	private final static byte eCmdSetFilterData = (byte)0x74;
	private final static byte eNotifyTagData = (byte)0x80;
	private final static byte eNotifyTagRecordData = (byte)0x82;
	private final static byte eNotifyRecvHeartbeats = (byte)0x90;
	private final static byte eCmdPreUpdate = (byte)0xF4;

	private final static byte eTagOpTypeRead = (byte)0;
	private final static byte eTagOpTypeWrite = (byte)1;
	private final static byte eTagOpTypeLock = (byte)2;
	private final static byte eTagOpTypeKill = (byte)3;
	public MRfidReader()
	{
		readerId = new byte[2];
		readerId[0] = 0;
		readerId[1] = 0;
	}
	
	private byte CaculateCheckSum(byte []message,int start_pos,int len)
	{
		long checksum = 0;
		int iIndex = 0;
		for (iIndex = 0;iIndex < len; iIndex++)
		{
			checksum += getUnsignedByte(message[start_pos+iIndex]);
		}
		checksum = ~checksum + 1;
		return (byte)(checksum & 0xFF);
	}

	private void FillLengthAndCheksum() {
		int indeedLen = sendIndex - 8;
		sendMsgBuff[6] = (byte)(indeedLen >> 8);
		sendMsgBuff[7] = (byte)indeedLen;
		sendMsgBuff[sendIndex] = CaculateCheckSum(sendMsgBuff, 0, sendIndex);
		++sendIndex;
	}
	
	private void BuildMessageHeader(byte commandCode) {
		// TODO Auto-generated method stub
		sendIndex = 0;
		sendMsgBuff[sendIndex++] = 'R';
		sendMsgBuff[sendIndex++] = 'F';
		sendMsgBuff[sendIndex++] = 0;
		sendMsgBuff[sendIndex++] = readerId[0];
		sendMsgBuff[sendIndex++] = readerId[1];
		sendMsgBuff[sendIndex++] = commandCode;
		//fill length zero
		sendMsgBuff[sendIndex++] = 0;
		sendMsgBuff[sendIndex++] = 0;
	}
	
	@Override
	public int Inventory() throws IOException {
		// TODO Auto-generated method stub
		if (null == transport)
		{
			return -1;
		}
		BuildMessageHeader((byte)0x21);
		FillLengthAndCheksum();
		transport.SendData(sendMsgBuff, sendIndex);
		return 0;
	}

	@Override
	public int InventoryOnce() throws IOException {
		// TODO Auto-generated method stub
		if (null == transport)
		{
			return -1;
		}
		BuildMessageHeader((byte)0x22);
		FillLengthAndCheksum();
		transport.SendData(sendMsgBuff, sendIndex);
		return 0;
	}

	@Override
	public int Stop() throws IOException {
		// TODO Auto-generated method stub
		if (null == transport)
		{
			return -1;
		}
		BuildMessageHeader((byte)0x23);
		FillLengthAndCheksum();
		transport.SendData(sendMsgBuff, sendIndex);
		return 0;
	}

	@Override
	public int Reset() throws IOException {
		// TODO Auto-generated method stub
		if (null == transport)
		{
			return -1;
		}
		BuildMessageHeader((byte)0x10);
		FillLengthAndCheksum();
		transport.SendData(sendMsgBuff, sendIndex);
		return 0;
	}

	@Override
	public int ReadTagBlock(byte membank, byte addr, byte len,byte[] accessPassword) throws IOException {
		// TODO Auto-generated method stub
		if (null == transport)
		{
			return -1;
		}
		BuildMessageHeader((byte)eCmdReadTagBlock);
		sendMsgBuff[sendIndex++] = (byte)eTlvAttrTagOperation;
		sendMsgBuff[sendIndex++] = (byte)(8 * len * 2);	//tlv长度

		if (accessPassword == null)
		{
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
		}
		else
		{
			if (accessPassword.length < 4)
			{
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
			}
			else
			{
				sendMsgBuff[sendIndex++] = accessPassword[0];
				sendMsgBuff[sendIndex++] = accessPassword[1];
				sendMsgBuff[sendIndex++] = accessPassword[2];
				sendMsgBuff[sendIndex++] = accessPassword[3];
			}
		}

		sendMsgBuff[sendIndex++] = eTagOpTypeRead;	//读操作
		sendMsgBuff[sendIndex++] = membank;
		sendMsgBuff[sendIndex++] = (byte)(addr >> 8);
		sendMsgBuff[sendIndex++] = (byte)(addr&0xFF);
		sendMsgBuff[sendIndex++] = len;
		FillLengthAndCheksum();
		transport.SendData(sendMsgBuff, sendIndex);
		return 0;
	}


	@Override
	public int WriteTagBlock(byte membank, int addr, byte len, byte[] writtenData, int writeStartIndex,byte[] accessPassword)
			throws IOException {
		// TODO Auto-generated method stub
		if (null == transport)
		{
			return -1;
		}
		BuildMessageHeader((byte)eCmdWriteTag);
		sendMsgBuff[sendIndex++] = (byte)eTlvAttrTagOperation;
		sendMsgBuff[sendIndex++] = (byte)(8 * len * 2);	//tlv长度

		if (accessPassword == null)
		{
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
		}
		else
		{
			if (accessPassword.length < 4)
			{
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
			}
			else
			{
				sendMsgBuff[sendIndex++] = accessPassword[0];
				sendMsgBuff[sendIndex++] = accessPassword[1];
				sendMsgBuff[sendIndex++] = accessPassword[2];
				sendMsgBuff[sendIndex++] = accessPassword[3];
			}
		}

		sendMsgBuff[sendIndex++] = eTagOpTypeWrite;	//写操作
		sendMsgBuff[sendIndex++] = membank;
		sendMsgBuff[sendIndex++] = (byte)(addr >> 8);
		sendMsgBuff[sendIndex++] = (byte)(addr&0xFF);
		sendMsgBuff[sendIndex++] = len;
		for (int index = 0; index < len*2; index++)
		{
			sendMsgBuff[sendIndex++] = writtenData[index];
		}
		FillLengthAndCheksum();
		transport.SendData(sendMsgBuff, sendIndex);
		return 0;
	}

	@Override
	public int LockTag(byte lockType,byte[] accessPassword) throws IOException {
		// TODO Auto-generated method stub

		if (null == transport)
		{
			return -1;
		}
		BuildMessageHeader((byte)eCmdLockTag);
		sendMsgBuff[sendIndex++] = (byte)eTlvAttrTagOperation;
		sendMsgBuff[sendIndex++] = 8;	//tlv长度

		if (accessPassword == null)
		{
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
		}
		else
		{
			if (accessPassword.length < 4)
			{
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
			}
			else
			{
				sendMsgBuff[sendIndex++] = accessPassword[0];
				sendMsgBuff[sendIndex++] = accessPassword[1];
				sendMsgBuff[sendIndex++] = accessPassword[2];
				sendMsgBuff[sendIndex++] = accessPassword[3];
			}
		}

		sendMsgBuff[sendIndex++] = eTagOpTypeLock;	//锁定操作
		sendMsgBuff[sendIndex++] = lockType;
		sendMsgBuff[sendIndex++] = 0;
		sendMsgBuff[sendIndex++] = 0;
		FillLengthAndCheksum();
		transport.SendData(sendMsgBuff, sendIndex);
		return 0;
	}

	@Override
	public int KillTag(byte[] accessPassword,byte[] killPassword) throws IOException {
		// TODO Auto-generated method stub
		if (null == transport)
		{
			return -1;
		}
		BuildMessageHeader((byte)eCmdLockTag);
		sendMsgBuff[sendIndex++] = (byte)eTlvAttrTagOperation;
		sendMsgBuff[sendIndex++] = 12;	//tlv长度

		if (accessPassword == null)
		{
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
			sendMsgBuff[sendIndex++] = 0;
		}
		else
		{
			if (accessPassword.length < 4)
			{
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
				sendMsgBuff[sendIndex++] = 0;
			}
			else
			{
				sendMsgBuff[sendIndex++] = accessPassword[0];
				sendMsgBuff[sendIndex++] = accessPassword[1];
				sendMsgBuff[sendIndex++] = accessPassword[2];
				sendMsgBuff[sendIndex++] = accessPassword[3];
			}
		}

		sendMsgBuff[sendIndex++] = eTagOpTypeLock;	//锁定操作
		sendMsgBuff[sendIndex++] = eTagOpTypeKill;
		sendMsgBuff[sendIndex++] = 0;
		sendMsgBuff[sendIndex++] = 0;

		//add kill password
		if ((killPassword != null) && (killPassword.length > 4)) {
			sendMsgBuff[sendIndex++] = killPassword[0];
			sendMsgBuff[sendIndex++] = killPassword[1];
			sendMsgBuff[sendIndex++] = killPassword[2];
			sendMsgBuff[sendIndex++] = killPassword[3];
		}

		FillLengthAndCheksum();
		transport.SendData(sendMsgBuff, sendIndex);
		return 0;
	}

	@Override
	public int HandleRecv() throws IOException {
		// TODO Auto-generated method stub
		try {
			recvMsgLen = transport.ReadData(recvMsgBuff);
		}
		catch(Exception e) {
			//the data is too lager
			System.out.println(e.toString());
			return 0;
		}
		HandleMessage();
		return 0;
	}

	@Override
	public void HandleMessage() {
		// TODO Auto-generated method stub
		byte []message = recvMsgBuff;
		byte checksum = 0;
		byte caculatedChecksum = 0;
		int buffPos = 0;
		int paramLen = 0;
		while (buffPos <= recvMsgLen - 9){
			if ( message[buffPos] != 'R' && message[buffPos+1] != 'F')
			{
				buffPos++;
			}
			
			paramLen = getUnsignedByte(message[buffPos+6]);
			paramLen = paramLen << 8;
			paramLen = paramLen + getUnsignedByte(message[buffPos+7]);
			if (paramLen > 255) {
				buffPos++;
				continue;
			}
			
			checksum = message[buffPos + paramLen + 8];
			caculatedChecksum = CaculateCheckSum(message,buffPos,paramLen+8);
			if (caculatedChecksum == checksum)
			{
				//上报接收到的指令
				NotifyMessageToApp(message, buffPos);
				//跳转到下一条指令
				buffPos = buffPos + paramLen + 9;
			}
			else {
				++buffPos;
			}
		}
	}

	@Override
	protected void NotifyMessageToApp(byte[] message, int startIndex) {
		// TODO Auto-generated method stub
		MRfidReaderNotifyImpl appNotify = (MRfidReaderNotifyImpl)getAppNotify();
		if ( null == appNotify) {
			return;
		}
		if ( 1 == message[startIndex + 2])
		{
			switch(message[startIndex + 5]) {
				case eCmdReset:
					appNotify.NotifyReset(message, startIndex);
					break;
				case eCmdWriteTag:
					appNotify.NotifyWriteTagBlock(message,startIndex);
					break;
				case eCmdReadTagBlock:
					appNotify.NotifyReadTagBlock(message,startIndex);
					break;
				default:
					System.out.println("Unhandled message ID:"+ String.format("%02X", message[startIndex + 5]));
					break;
			}
		}
		else if (2 == message[startIndex + 2])
		{
			switch(message[startIndex+5])
			{
				case eNotifyTagData:
					appNotify.NotifyRecvTags(message, startIndex);
				break;
				default:
					System.out.println("Unhandled notify message ID:"+ String.format("%02X", message[startIndex + 5]));
					break;
			}
		}
	}

	@Override
	public int RelayControl(int relayNo, int operation,int time) throws IOException {
		// TODO Auto-generated method stub
		//relayNo:控制继电器1:1 控制继电器2:2  控制继电器1和继电器2:3
		//operation: 0:关闭  1:打开
		//time:继电器打开的时间
		if (null == transport)
		{
			return -1;
		}
		BuildMessageHeader((byte)eCmdCtrlRelay);
		sendMsgBuff[sendIndex++] = (byte)eTlvAttrRelay;
		sendMsgBuff[sendIndex++] = (byte)0x03;
		sendMsgBuff[sendIndex++] = (byte)relayNo;
		sendMsgBuff[sendIndex++] = (byte)operation;
		sendMsgBuff[sendIndex++] = (byte)time;
		FillLengthAndCheksum();
		transport.SendData(sendMsgBuff, sendIndex);
		return 0;
	}

}
